#include "fibevent.h"

#include "BaseFiberServer.h"
#include "IocpServerSocketDispatcher.h"
#include "IocpServerSocket.h"
#include "BaseFiberClientHandler.h"
#include "wxtl/Fiber.h"
#include "log.h"

void BaseFiberServer::threadProc()
{
	Fiber::Init();
	while(true)
	{
		wxtl::Socket newSocket;
		try
		{
			m_socket.accept(&newSocket);
			SOCKET s = newSocket.get();
			IocpServerSocket* client = new IocpServerSocket(newSocket);

			m_completionPort.Associate((HANDLE)s, (ULONG_PTR) client);
			PostInitStatus(client);
		}
		catch(...)
		{
			break;
		}			
	}
}

BaseFiberServer::BaseFiberServer()
	:m_maxIocpThreads(2)
{
	

}

void BaseFiberServer::Listen( UINT port )
{
	if( m_stop.isValid() && !m_stop.wait(0))
	{
		LERROR("Calling Listen() while already listening");
		abort();
	}
	m_stop.create(true);
	m_stopListen.create(true);
	
	LINFO("Allocating " << m_maxIocpThreads << " Iocp handler threads");

	for(size_t i = 0; i < m_maxIocpThreads; i++)
	{		
		try
		{
			//problems, officer? these will delete them selfs  after ShutDown() called
			new IocpServerSocketDispatcher(&m_completionPort, this);
		}
		catch (const wxtl::_win32_error& e)
		{
			e;
			LFATAL("Exception while allocating iocp handler threads. Win error: " << e.error());
			abort();
		}
		
	}
	
	m_socket.create(wxtl::Socket::Stream, true);
	m_socket.bind(port);
	m_socket.listen();
	create();
}

void BaseFiberServer::ShutDown()
{	
	m_socket.close();//stop new connections
	WaitForFibers();//wait for existing fibers to finish
	m_stop.set();//stop dispatchers
	wait();

	m_stop.close();
	close();
}

void BaseFiberServer::PostInitStatus( IocpServerSocket* client )
{
	IocpOperation* overlapped = new IocpOperation(IocpOperation::Init, NULL);
	m_completionPort.PostStatus(0,(ULONG_PTR) client, (LPOVERLAPPED)overlapped);
}

void BaseFiberServer::PostTimeoutStatus( IocpServerSocket*  )
{
	LFATAL("Not implemented");
	abort();
}

wxtl::Event& BaseFiberServer::GetStopEvent()
{
	return m_stop;
}

void BaseFiberServer::SetMaxIocpThreads( size_t maxIocpThreads )
{
	m_maxIocpThreads = maxIocpThreads;
}

void BaseFiberServer::IncreaseFibersNumber()
{
	InterlockedIncrement(&m_numFibers);
}

void BaseFiberServer::DecreaseFibersNumber()
{
	InterlockedDecrement(&m_numFibers);
}

void BaseFiberServer::WaitForFibers()
{
	while(m_numFibers)
	{
		//shame
		Sleep(10);
	}
}

IocpServerSocket* BaseFiberServer::CreateServerSocket()
{
	wxtl::Socket newSocket;
	newSocket.create(wxtl::Socket::Stream, true);
	SOCKET s = newSocket.get();
	IocpServerSocket* client = new IocpServerSocket(newSocket);
	m_completionPort.Associate((HANDLE)s, (ULONG_PTR) client);
	return client;
}

BaseFiberClientHandler* BaseFiberServer::CreateClient()
{
	BaseFiberClientHandler* client = CreateFiberClientHandler();
	client->SetServer(this);
	return client;
}

void BaseFiberServer::Initialize()
{
	InitLog();
	wxtl::Socket::startUp();
	Fiber::Init();
}

UINT BaseFiberServer::GetPort()
{
	return m_socket.getLocalAddr().getPort();
}

